package github.sf.mod.calculate;


import github.sf.mod.calculate.entity.Binary;
import github.sf.mod.calculate.entity.Expression;
import github.sf.mod.calculate.entity.NumberLiteral;

import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

public class Parser {
    private Scanner scanner;
    //    private final static Map<String, Integer> opPrec = new HashMap<>();
    private final static Map<Op, Integer> opPrec = new HashMap<>();

    static {
//        opPrec.put("+", 1);
//        opPrec.put("-", 1);
//        opPrec.put("*", 2);
//        opPrec.put("/", 2);
//        opPrec.put("=", 0);
        opPrec.put(Op.Plus, 1);
        opPrec.put(Op.Minus, 1);
        opPrec.put(Op.Multiply, 2);
        opPrec.put(Op.Divide, 2);
    }

    /**
     * 分析
     */
    public Parser(Scanner scanner) {
        this.scanner = scanner;
//        scanner.next();
    }

    public Expression parse() {
        int assignPrec = 0;
        //先解析一个优先级更高的表达式
        Expression exp1 = parserBinary(assignPrec);
        Token t = scanner.peek();
        Integer tprec = opPrec.get(t.getCode());
        //存放赋值运算符两边的表达式
        Stack<Expression> expStack = new Stack<>();
        expStack.push(exp1);
        //存放运算符
        Stack<Op> opStack = new Stack<>();
        //解析赋值表达式
        while (t.getType() == TokenType.Operation && tprec == assignPrec) {
            opStack.push(t.getCode());
            this.scanner.next();//跳过运算符
            exp1 = parserBinary(assignPrec);
            t = scanner.peek();
            tprec = opPrec.get(t.getCode());
        }
        //组装成为右结合AST
        exp1 = expStack.get(expStack.size() - 1);
        if (opStack.size() > 0) {
            for (int i = expStack.size() - 2; i >= 0; i--) {
                exp1 = new Binary(opStack.get(i), expStack.get(i), exp1);
            }
        }
        return exp1;
    }

    public Expression parsePrimary() {
        Token token = scanner.peek();
        if (token.getType() == TokenType.DecimalLiteral || token.getType() == TokenType.IntegerLiteral) {
            scanner.next();
            return new NumberLiteral(token.getText());
        } else if (token.getType() == TokenType.OpenParen) {
            scanner.next();
            Expression exp = parserExpression();
            if (scanner.peek().getType() == TokenType.CloseParen) {
                scanner.next();
            } else {
                System.err.println("少一个括号");
                throw new RuntimeException("少一个括号:" + exp);
            }
            return exp;
        }
        //理论上不会执行到这里来
        return null;
    }

    public Expression parserUnary() {
        Token t = scanner.peek();
        return parsePrimary();
    }

    /**
     * 采用运算符优先级算法，解析二元表达式
     * 这是一个递归算法。一开始，提供的参数是最低优先级
     *
     * @param prec 当前运算符的优先级
     */
    public Expression parserBinary(int prec) {
        Expression exp1 = parserUnary();
        Token t = scanner.peek();
        Integer tprec = getPrec(t.getCode());
        //下面这个循环的意思是：只要右边出现的新运算符的优先级更高，
        //那么就把右边出现的作为右子节点。
        /**
         * 对于2+3*5
         * 第一次循环，遇到+号，优先级大于零，所以做一次递归的parseBinary
         * 在递归的binary中，遇到乘号，优先级大于+号，所以形成3*5返回，又变成上一级的右子节点。
         *
         * 反过来，如果是3*5+2
         * 第一次循环还是一样，遇到*号，做一次递归的parseBinary
         * 在递归中，新的运算符的优先级要小，所以只返回一个5，跟前一个节点形成3*5,成为新的左子节点。
         * 接着做第二次循环，遇到+号，返回5，并作为右子节点，跟3*5一起组成一个新的binary返回。
         */
        while (t.getType() == TokenType.Operation && tprec > prec) {
            //跳过运算符
            scanner.next();
            Expression exp2 = parserBinary(tprec);
            exp1 = new Binary(t.getCode(), exp1, exp2);
            t = scanner.peek();
            tprec = getPrec(t.getCode());
        }
        return exp1;
    }

    public Expression parserExpression() {
        return parse();
    }

    public Integer getPrec(Op op) {
        return opPrec.get(op);
    }
}
